
'''
    Software License

    Copyright (C) 2021-05-24  Xoronos

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, version 3.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <https://www.gnu.org/licenses/>.
'''

'''
    Liabilities

    The software is provided "AS IS" without any warranty of any kind, either expressed,
    implied, or statutory, including, but not limited to, any warranty that the software
    will conform to specifications, any implied warranties of merchantability, fitness
    for a particular purpose, and freedom from infringement, and any warranty that the
    documentation will conform to the software, or any warranty that the software will
    be error free.

    In no event shall Xoronos be liable for any damages, including, but not limited to,
    direct, indirect, special or consequential damages, arising out of, resulting from,
    or in any way connected with this software, whether or not based upon warranty,
    contract, tort, or otherwise, whether or not injury was sustained by persons or
    property or otherwise, and whether or not loss was sustained from, or arose out of
    the results of, or use of, the software or services provided hereunder.
    
    To request the provided software under a different license you can contact us at
    support@xoronos.com
'''

#####################################################
##                   Introduction                  ##
#####################################################
## This code example shows how to encrypt bytes    ##
## with a binomial key and decrypt with bytes      ##
## with a monomial key.                            ##
##                                                 ##
## The algorithm is a stream cypher algorithm so   ##
## all the input/output bytes will be              ##
## encrypted/decrypted.                            ##
##                                                 ##
## Furthermore, data integrity checks are included ##
## in the encryption algorithm.                    ##
#####################################################

#####################################################
##                   Pre-requisits                 ##
#####################################################
## The Xoron matrix decompressed data structure    ##
## needs to be mined before running this code      ##
## example. Some examples on how to mine Xoron     ##
## matrices can be found in                        ##
## generate_xrn_matrix_all_rnd.py or               ##
## generate_xrn_matrix_one_third.py                ##
##                                                 ##
## A monomial key. An example on how to generate   ##
## keys can be found in                            ##
## generate_keys_with_wrapper_functions.py         ##
##                                                 ##
## A binomial key. An example on how to generate   ##
## keys can be found in                            ##
## generate_keys_with_wrapper_functions.py         ##
#####################################################

import xrnlib256 as xrn
import lorem_ipsum as li


def xrn_encrypt_asymetric_bytes_wrapper_example( ) :

    ##########################
    ## Variable definitions ##
    ##########################

    # library run time settings

    settings = xrn.xrn_crypto_extra_settings_t ( ) 
    # all the subfields of settings, can be found in xrn_crypto_extra_settings_t.txt

    xtype = xrn.new_xrn_lib_xtype_tp()
    len_output = xrn.new_uint64p ()
    num_encrypted_bytes = xrn.new_uint64p ()

    ##########################
    ## Initialize variables ##
    ##########################

    # Initialize default run time settings

    if ( xrn.XSUCCESS != xrn.xrn_load_default_settings( settings ) ) :
        xrn.delete_xrn_lib_xtype_tp( xtype )
        xrn.delete_uint64p ( len_output )
        xrn.delete_uint64p ( num_encrypted_bytes )
        return -1

    # Default settings can be changed, for example
    # settings.crc_bytes = 1 
    # settings.cnt_bytes = 8 
    # settings.state_size = 122 
    # strcpy(settings.rnd_str,"my_string") 

    # Get the type of bytes from the function name

    if ( xrn.XSUCCESS != xrn.xrn_name_2_type( "xrn_encrypt_asymmetric_bytes_wrapper", xtype, settings ) ) :
        xrn.delete_xrn_lib_xtype_tp( xtype )
        xrn.delete_uint64p ( len_output )
        xrn.delete_uint64p ( num_encrypted_bytes )
        return -1

    # Estimate the length of the output buffer
    if ( xrn.XSUCCESS != xrn.xrn_estimate_encryption_bytes_length( xrn.xrn_lib_xtype_tp_value(xtype), len( li.LOREM_IPSUM_STR ), len_output, settings ) ) :
        xrn.delete_xrn_lib_xtype_tp( xtype )
        xrn.delete_uint64p ( len_output )
        xrn.delete_uint64p ( num_encrypted_bytes )
        return -1
    
    xrn_matrix_fp = xrn.fopen( "xrn_matrix.xm", "rb" )
    binomial_key_fp = xrn.fopen( "xbinomial_key.xf", "rb" )
    if (( binomial_key_fp == None ) or ( xrn_matrix_fp == None ) ):
        xrn.delete_xrn_lib_xtype_tp( xtype )
        xrn.delete_uint64p ( len_output )
        xrn.delete_uint64p ( num_encrypted_bytes )
        return -1

    encrypted_bytes = xrn.new_uint8p( xrn.uint64p_value(len_output) )

    # to know the number of bytes of plain_text in each block you can run
    # uint64_t bytes_in_block =  xrn.xrn_estimate_decrypted_block_bytes_length( settings )

    if ( xrn.XSUCCESS != xrn.xrn_encrypt_asymmetric_bytes_wrapper( li.LOREM_IPSUM_ARRAY, # input bytes
                                                       len( li.LOREM_IPSUM_STR ),        # length of the input bytes
                                                       encrypted_bytes,        # encrypted bytes
                                                       xrn.uint64p_value(len_output),      # length of buffer
                                                       num_encrypted_bytes,   # number of encrypted bytes
                                                       xrn_matrix_fp, # Xoron matrix
                                                       binomial_key_fp, # binomial key
                                                       settings # runtime settings
          ) ) :

        xrn.delete_xrn_lib_xtype_tp( xtype )
        xrn.delete_uint64p ( len_output )
        xrn.delete_uint64p ( num_encrypted_bytes )
        xrn.delete_uint8p ( encrypted_bytes )
        xrn.fclose( binomial_key_fp )
        xrn.fclose( xrn_matrix_fp )
        return -1

    

    if ( ( 0 != xrn.fclose( xrn_matrix_fp ) ) or ( 0 != xrn.fclose( binomial_key_fp ) ) ) :
        xrn.delete_xrn_lib_xtype_tp( xtype )
        xrn.delete_uint64p ( len_output )
        xrn.delete_uint64p ( num_encrypted_bytes )
        xrn.delete_uint8p ( encrypted_bytes );
        return -1

    # An alternative way to perform the same computation can be achieved using the xrn_encrypt_asymmetric_bytes function
    # if ( xrn.XSUCCESS != xrn.xrn_encrypt_asymmetric_bytes( li.LOREM_IPSUM_ARRAY, # input bytes
    #                                                    len( li.LOREM_IPSUM_STR ),        # length of the input bytes
    #                                                    encrypted_bytes,        # encrypted bytes
    #                                                    xrn.uint64p_value(len_output),      # length of buffer
    #                                                    num_encrypted_bytes,   # number of encrypted bytes
    #                                                    xrn_matrix, # Xoron matrix is of the type ( xrn_matrix_t * )
    #                                                    binomial_key, # binomial key is of the type ( xbinomial_key_t * )
    #                                                    settings # runtime settings
    #       ) ) :

    xrn.delete_xrn_lib_xtype_tp( xtype )
    xrn.delete_uint64p ( len_output )

    return [ num_encrypted_bytes , encrypted_bytes ]

def xrn_decrypt_asymetric_bytes_wrapper_example( input_encrypted_bytes, num_encrypted_bytes ) :

    ##########################
    ## Variable definitions ##
    ##########################

    # library run time settings

    settings = xrn.xrn_crypto_extra_settings_t ( )
    # all the subfields of settings, can be found in xrn_crypto_extra_settings_t.txt

    xtype = xrn.new_xrn_lib_xtype_tp()
    len_decrypted_bytes = xrn.new_uint64p ()
    num_decrypted_bytes = xrn.new_uint64p ()

    ##########################
    ## Initialize variables ##
    ##########################

    # Initialize default run time settings

    if ( xrn.XSUCCESS != xrn.xrn_load_default_settings( settings ) ) :
        xrn.delete_xrn_lib_xtype_tp( xtype )
        xrn.delete_uint64p ( len_output )
        xrn.delete_uint64p ( num_encrypted_bytes )
        return -1

    # Get the type of bytes from the function name

    if ( xrn.XSUCCESS != xrn.xrn_name_2_type( "xrn_decrypt_asymmetric_bytes_wrapper", xtype, settings ) ) :
        xrn.delete_xrn_lib_xtype_tp( xtype )
        xrn.delete_uint64p( len_decrypted_bytes )
        xrn.delete_uint64p( num_decrypted_bytes )
        return -1

    # Estimate the length of the output buffer
    if ( xrn.XSUCCESS != xrn.xrn_estimate_decryption_bytes_length( xrn.xrn_lib_xtype_tp_value(xtype), xrn.uint64p_value(num_encrypted_bytes), len_decrypted_bytes, settings ) ) :
        xrn.delete_xrn_lib_xtype_tp( xtype )
        xrn.delete_uint64p( len_decrypted_bytes )
        xrn.delete_uint64p( num_decrypted_bytes )
        return -1

    xrn_matrix_fp = xrn.fopen( "xrn_matrix.xm", "rb" )
    monomial_key_fp = xrn.fopen( "xmonomial_key.xf", "rb" )

    if (( monomial_key_fp == None ) or ( xrn_matrix_fp == None )) :
        xrn.delete_xrn_lib_xtype_tp( xtype )
        xrn.delete_uint64p( len_decrypted_bytes )
        xrn.delete_uint64p( num_decrypted_bytes )
        return -1

    decrypted_bytes = xrn.new_uint8p( xrn.uint64p_value( len_decrypted_bytes ) )

    # to know the number of bytes of plain_text in each block you can run
    # uint64_t bytes_in_block =  xrn_estimate_decrypted_block_bytes_length( settings )

    if ( xrn.XSUCCESS != xrn.xrn_decrypt_asymmetric_bytes_wrapper( input_encrypted_bytes,   # input bytes
                                                       xrn.uint64p_value(num_encrypted_bytes),     # input bytes length
                                                       decrypted_bytes,        # decrypted bytes
                                                       xrn.uint64p_value(len_decrypted_bytes),     # length of the buffer of decrypted bytes
                                                       num_decrypted_bytes,     # the number of decrypted bytes
                                                       xrn_matrix_fp, # Xoron matrix file pointer
                                                       monomial_key_fp, # monomial key file pointer
                                                       settings # runtime settings
          ) ) :
        xrn.delete_xrn_lib_xtype_tp( xtype )
        xrn.delete_uint64p( len_decrypted_bytes )
        xrn.delete_uint64p( num_decrypted_bytes )
        xrn.delete_uint8p(decrypted_bytes)
        xrn.fclose( xrn_matrix_fp )
        xrn.fclose( monomial_key_fp )
        return -1


    if ( ( 0 != xrn.fclose( xrn_matrix_fp ) ) or ( 0 != xrn.fclose( monomial_key_fp ) ) ) :
        xrn.delete_xrn_lib_xtype_tp( xtype )
        xrn.delete_uint64p( len_decrypted_bytes )
        xrn.delete_uint64p( num_decrypted_bytes )
        xrn.delete_uint8p(decrypted_bytes)
        return -1

    # An alternative way to perform the same computation can be achieved using the xrn_decrypt_asymmetric_bytes function
    # if ( xrn.XSUCCESS != xrn_decrypt_asymmetric_bytes( input_encrypted_bytes,
    #                                           num_encrypted_bytes,
    #                                           *decrypted_bytes,
    #                                           len_decrypted_bytes,
    #                                           num_decrypted_bytes,
    #                                           xrn_matrix, # Xoron matrix is of the type ( xrn_matrix_t *)
    #                                           monomial_key, # monomial key is of the type ( xmonomial_key_t )
    #                                           settings
    #      ) ) :

    xrn.delete_xrn_lib_xtype_tp( xtype )
    xrn.delete_uint64p( len_decrypted_bytes )

    return [ num_decrypted_bytes, decrypted_bytes ]



#############
## Logging ##
#############

# Set the default logging file streams
# errors -> stderr
# warnings -> stdout
# notifications -> stdout
# debug -> stdout

xrn.xrn_set_default_log(  )

# To change the default logging you can use the functions below,
# where fp is the opened file pointer
# 
# xrn_change_error_log( fp )
# xrn_change_warning_log( fp )
# xrn_change_notification_log( fp )
# xrn_change_debug_log( fp )

print( "encryption... " )
[ num_encrypted_bytes , encrypted_bytes ] = xrn_encrypt_asymetric_bytes_wrapper_example( )
if ( -1 == num_encrypted_bytes  ) :
    print( "error in encryption " )
    exit( -1 )

print( "decryption... " )
[ num_decrypted_bytes , decrypted_bytes ] = xrn_decrypt_asymetric_bytes_wrapper_example( encrypted_bytes, num_encrypted_bytes )
if ( -1 == num_decrypted_bytes ):
    xrn.delete_uint64p(num_encrypted_bytes );
    xrn.delete_uint8p(encrypted_bytes );
    print( "error in decryption " )
    exit( -1 )

fp = xrn.fopen( "decrypted_file.txt", "wb" )
if ( fp == None ) :
    xrn.delete_uint64p(num_encrypted_bytes );
    xrn.delete_uint64p(num_decrypted_bytes );
    xrn.delete_uint8p(encrypted_bytes );
    xrn.delete_uint8p(decrypted_bytes );
    exit( -1 )

if ( 1 != xrn.fwrite( decrypted_bytes, xrn.uint64p_value(num_decrypted_bytes), 1, fp ) ) :
    xrn.delete_uint64p(num_encrypted_bytes );
    xrn.delete_uint64p(num_decrypted_bytes );
    xrn.delete_uint8p(encrypted_bytes );
    xrn.delete_uint8p(decrypted_bytes );
    xrn.fclose( fp )
    exit( -1 )

xrn.delete_uint64p(num_encrypted_bytes );
xrn.delete_uint64p(num_decrypted_bytes );
xrn.delete_uint8p(encrypted_bytes );
xrn.delete_uint8p(decrypted_bytes );
xrn.fclose( fp )
print( "decrypted file in cache/examples/exec/decrypted_file.txt" )

exit( 0 )



